#include <iostream>
#include "data_structure.h"
#include <sstream>
#include <fstream>

int label = 1;
int temp=1;
Node node;

#define IDN "IDN"
#define INT10 "INT10"
#define INT8 "INT8"
#define INT16 "INT16"
#define OP "OP"
#define KEY "KEY"
#define REAL10 "REAL10"
#define REAL8 "REAL8"
#define REAL16 "REAL16"

bool is16(char a) {
    if (isalnum(a)) {
        return true;
    }
    else if (a >= 'a' && a <= 'f') {
        return true;
    }
    return false;
}

bool is8(char a) {
    if (a >= '0' && a < '8') {
        return true;
    }
    return false;
}

bool isOperator(char a) {
    switch (a) {
        case '>':
        case '<':
        case '=':
        case '+':
        case '-':
        case '*':
        case '/':
        case '(':
        case ')':
        case ';':
            return true;
        default:return false;
    }
}

bool isKeyWord(std::string a) {//if   then   else   while  do
    if (a == "if") {
        return true;
    }
    if (a == "then") {
        return true;
    }
    if (a == "else") {
        return true;
    }
    if (a == "while") {
        return true;
    }
    if (a == "do") {
        return true;
    }
    return false;
}

double Hex2Dec(std::string s){
    int count = s.length();
    double sum = 0;
    int j = count;
    for (int i = 0; i < count; i++) {
        if (s[i] == '.') {
            j = i;
            break;
        }
    }
    for (int i = j - 1; i >= 2; i--){
        if (s[i] >= '0' && s[i] <= '9'){
            sum += (s[i] - 48) * pow(16, j - i - 1);
        }
        else if (s[i] >= 'A' && s[i] <= 'F'){
            sum += (s[i] - 55) * pow(16, j - i - 1);
        }
        else if (s[i] >= 'a' && s[i] <= 'f'){
            sum += (s[i] - 87) * pow(16, j - i - 1);
        }
    }
    for (int i = j + 1; i < count; i++){
        if (s[i] >= '0' && s[i] <= '9'){
            sum += (s[i] - 48) * pow(16, j - i);
        }
        else if (s[i] >= 'A' && s[i] <= 'F'){
            sum += (s[i] - 55) * pow(16, j - i);
        }
        else if (s[i] >= 'a' && s[i] <= 'f'){
            sum += (s[i] - 87) * pow(16, j - i);
        }
    }
    return sum;
}

double Oct2Dec(std::string s) {

    int count = s.length();
    double sum = 0;
    int j = count;
    for (int i = 0; i < count; i++) {
        if (s[i] == '.') {
            j = i;
            break;
        }
    }
    for (int i = j - 1; i >= 2; i--){
        if (s[i] >= '0' && s[i] <= '8'){
            sum += (s[i] - 48) * pow(8, j - i - 1);
        }
    }
    for (int i = j + 1; i < count; i++){
        if (s[i] >= '0' && s[i] <= '8'){
            sum += (s[i] - 48) * pow(8, j - i);
        }
    }
    return sum;
}

int scan(std::ifstream& in_file,std::ofstream& out_file){
    std::string str;
    char ch;
    int num = 0;
    std::string kind;
    if (getline(in_file,str)) {
        while (num < str.size()) {
            ch = str[num];
            if (ch == ' '){
                num++;
                kind.clear();
            }
            if (isalpha(ch)) {//IDN
                while (true) {
                    num++;
                    if (num != str.size()) {
                        ch = str[num];
                        if (isalpha(ch) || isalnum(ch)|| ch == '_') {
                            continue;
                        }
                    }
                    break;
                }
                kind = IDN;
            }
            else if (isalnum(ch) && ch != '0') {//INT10 or DOUBLE
                int INTorDOUBLE = 0;
                while (true)
                {
                    num++;
                    if (num != str.size()) {
                        ch = str[num];

                        if (isalnum(ch)) {
                            continue;
                        }
                        if (INTorDOUBLE == 0&&ch == '.'){
                            INTorDOUBLE = 1;
                            continue;
                        }
                    }
                    break;
                }
                if(INTorDOUBLE){
                    kind = REAL10;
                }
                else
                    kind = INT10;
            }
            else if (ch == '0') {//INT
                num++;
                if (num != str.size()) {
                    ch = str[num];
                    if (ch == 'o' || ch == 'O') {
                        int INTorDOUBLE = 0;
                        while (true)
                        {
                            num++;
                            if (num != str.size()) {
                                ch = str[num];
                                if (is8(ch)) {
                                    continue;
                                }
                                if (INTorDOUBLE == 0 && ch == '.') {
                                    INTorDOUBLE = 1;
                                    continue;
                                }
                            }
                            break;
                        }
                        if(INTorDOUBLE == 1){
                            kind = REAL8;
                        }
                        else
                            kind = INT8;
                    }
                    else if (ch == 'x' || ch == 'X') {
                        int INTorDOUBLE = 0;
                        while (true)
                        {
                            num++;
                            if (num != str.size()) {
                                ch = str[num];
                                if (is16(ch)) {
                                    continue;
                                }
                                if (INTorDOUBLE == 0 && ch == '.') {
                                    INTorDOUBLE = 1;
                                    continue;
                                }
                            }
                            break;
                        }
                        if(INTorDOUBLE == 1){
                            kind = REAL16;
                        }
                        else
                            kind = INT16;
                    }
                    else if(ch == '.') {
                        while (true){
                            num++;
                            if (num != str.size()){
                                ch = str[num];
                                if (isalnum(ch)){
                                    continue;
                                }
                            }
                            break;
                        }
                        kind = REAL10;

                    }
                }
                else {
                    kind = INT10;
                }
            }
            else if (isOperator(ch)) {
                num++;
                kind = OP;
            }

            std::string token = str.substr(0, num);
            if (kind == IDN && isKeyWord(token)) {
                kind = KEY;
            }

            out_file << std::fixed;
            out_file.precision(2);
            out_file.setf(std::ios_base::showpoint);
            if(kind.empty()){
                ;
            } else if (kind == OP||kind == KEY){
                out_file << token << std::endl << '_' << std::endl;
            }
            else if(kind == INT8 ){
                out_file << kind << std::endl << (int)(Oct2Dec(token)) << std::endl;
            }
            else if( kind == REAL8){
                out_file << kind << std::endl << Oct2Dec(token) << std::endl;
            }
            else if(kind == INT16) {
                out_file << kind << std::endl << (int)(Hex2Dec(token)) << std::endl;
            }
            else if(kind == REAL16) {
                out_file << kind << std::endl << Hex2Dec(token) << std::endl;
            }
            else{
                out_file << kind << std::endl << token << std::endl;
            }

            if (num != str.size()) {
                str = str.substr(num);
                num = 0;
                ch = str[num];
                continue;
            }
            break;
        }
        return 1;
    }
    return 0;
}

bool getNext(std::ifstream& fp){
    std::string str;
    std::getline(fp,str);
    node.setType(str);
    std::getline(fp,str);
    node.setValue(str);
    return !str.empty();
}

std::string newtemp(){
    char tmp[10];
    sprintf(tmp,"t%d",temp++);
    return tmp;
}

int newlabel(){
    return label++;
}

bool MyFunction_S(std::ifstream& fp,str_S &tmpS){
    str_E tmpE;
    str_C tmpC;
    str_S tmpS1;
    str_X tmpX;
    if (getNext(fp)) {
        //S->id=E;
        if (node.getType() == "IDN") {
            std::string tmpName = node.getValue();
            if (getNext(fp)) {
                if (node.getType() == "=") {
                    if (getNext(fp)) {
                        if (MyFunction_E(fp, tmpE)) {
                            tmpS.setCode(tmpE.getCode() + tmpName + " = " + tmpE.getPlace()+"\n");
                            if(node.getType()==";"){
                                return true;
                            }
                        }
                    }
                }
            }
        }
        //S->if C then S1 X;
        else if (node.getType() == "if") {
            if (getNext(fp)) {
                tmpC.setIsTrue(newlabel());
                tmpX.setBegin(tmpS.getNext());
                tmpC.setIsFalse(tmpX.getBegin());
                tmpS1.setNext(tmpX.getBegin());
                tmpX.setNext(tmpS1.getNext());
                if (MyFunction_C(fp, tmpC)) {
                    if (node.getType() == "then") {
                        if (MyFunction_S(fp, tmpS1)) {
                            if(MyFunction_X(fp,tmpX)){
                                if(tmpX.isIselse()){
                                    tmpS.setNext(tmpX.getNext());
                                    char tmp[500];
                                    sprintf(tmp,"%sL%d:\t%s%s",tmpC.getCode().c_str(),tmpC.getIsTrue(),tmpS1.getCode().c_str(),tmpX.getCode().c_str());
                                    tmpS.setCode(tmp);
                                }
                                else {
                                    char tmp[500];
                                    sprintf(tmp,"%s%s",tmpC.getCode().c_str(),tmpS1.getCode().c_str());
                                    tmpS.setCode(tmp);
                                }
                                if(node.getType()==";"){
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }
        //S->while C do S;
        else if (node.getType() == "while") {

            //分配标签
            tmpS.setBegin(newlabel());
            int tmplabel = tmpS.getBegin();
            if (getNext(fp)) {
                if (tmpS.getBegin() > 1) {
                    label--;
                    tmpS.setBegin(tmplabel-1);
                }
                tmpC.setIsTrue(newlabel());
                tmpS1.setBegin(tmpC.getIsTrue());
                tmpC.setIsFalse(tmpS.getNext());
                tmpS1.setNext(tmpS.getBegin());
                if (MyFunction_C(fp, tmpC)) {
                    //匹配do
                    if (node.getType() == "do") {
                        if (MyFunction_S(fp, tmpS1)) {
                            if(tmpS.getBegin()==1){
                                char tmp[500];
                                sprintf(tmp,"L%d:\t%sL%d:\t%s\tgoto L%d\n",tmpS.getBegin(),tmpC.getCode().c_str(),tmpC.getIsTrue(),tmpS1.getCode().c_str(),tmpS.getBegin());
                                tmpS.setCode(tmp);
                            } else{
                                char tmp[500];
                                sprintf(tmp,"%sL%d:\t%s\tgoto L%d\n",tmpC.getCode().c_str(),tmpC.getIsTrue(),tmpS1.getCode().c_str(),tmpS.getBegin());
                                tmpS.setCode(tmp);
                            }
                            if(node.getType()==";"){
                                return true;
                            }
                        }
                    }
                }
            }
        }
    }
    return false;
}

bool MyFunction_X(std::ifstream& fp,str_X &tmpX) {
    str_S tmpS;
    //X->else S
    if (node.getType() == "else") {
        tmpX.setNext(newlabel());
        tmpS.setNext(tmpX.getNext());
        tmpS.setBegin(tmpX.getBegin());
        if (MyFunction_S(fp, tmpS)) {
            char tmp[500];
            sprintf(tmp,"goto L%d\nL%d:\t%s\n\t",tmpX.getNext(),tmpX.getBegin(),tmpS.getCode().c_str());
            tmpX.setCode(tmp);
            tmpX.setIselse(true);
            return true;
        }
    }
    //X->null
    else if(node.getType()==";"){
        tmpX.setNext(tmpX.getBegin());
        tmpX.setCode("");
        tmpX.setIselse(false);
        return true;
    }
    return false;
}

bool MyFunction_C(std::ifstream& fp,str_C &tmpC){
    str_E tmpE1;
    str_E tmpE2;
    if(MyFunction_E(fp,tmpE1)){
        if(node.getType()==">"){
            if(getNext(fp)){
                if(MyFunction_E(fp,tmpE2)){
                    char tmp[500];
                    sprintf(tmp,"%s%sif %s > %s goto L%d\n\tgoto L%d\n",tmpE1.getCode().c_str(),tmpE2.getCode().c_str(),tmpE1.getPlace().c_str(),tmpE2.getPlace().c_str(),tmpC.getIsTrue(),tmpC.getIsFalse());
                    tmpC.setCode(tmp);
                    return true;
                }
            }
        }
        else if(node.getType()=="<") {
            if (getNext(fp)) {
                if (MyFunction_E(fp, tmpE2)) {
                    char tmp[500];
                    sprintf(tmp, "%s%sif %s < %s goto L%d\n\tgoto L%d\n", tmpE1.getCode().c_str(),
                            tmpE2.getCode().c_str(), tmpE1.getPlace().c_str(), tmpE2.getPlace().c_str(),
                            tmpC.getIsTrue(), tmpC.getIsFalse());
                    tmpC.setCode(tmp);
                    return true;
                }
            }
        }
        else if(node.getType()=="="){
            if (getNext(fp)) {
                if(MyFunction_E(fp,tmpE2)){
                    char tmp[500];
                    sprintf(tmp,"%s%sif %s = %s goto L%d\n\tgoto L%d\n",tmpE1.getCode().c_str(),tmpE2.getCode().c_str(),tmpE1.getPlace().c_str(),tmpE2.getPlace().c_str(),tmpC.getIsTrue(),tmpC.getIsFalse());
                    tmpC.setCode(tmp);
                    return true;
                }
            }
        }
    }
    return false;
}

bool MyFunction_E(std::ifstream& fp,str_E &tmpE){
    str_T tmpT1;
    str_T tmpT2;
    bool isFirst = true;
    if(MyFunction_T(fp,tmpT1)){
        while (1){
            if(node.getType()=="+") {
                isFirst = false;
                if(getNext(fp)){
                    if (MyFunction_T(fp, tmpT2)) {
                        tmpE.setPlace(newtemp());
                        char tmp[500];
                        sprintf(tmp, "%s%s%s = %s + %s\n\t", tmpT1.getCode().c_str(), tmpT2.getCode().c_str(),
                                tmpE.getPlace().c_str(), tmpT1.getPlace().c_str(), tmpT2.getPlace().c_str());
                        tmpE.setCode(tmp);
                        tmpT1.setCode(tmpE.getCode());
                        tmpT1.setPlace(tmpE.getPlace());
                    }
                }
            }
            else if(node.getType()=="-"){
                isFirst = false;
                if(getNext(fp)){
                    if (MyFunction_T(fp, tmpT2)) {
                        tmpE.setPlace(newtemp());
                        char tmp[500];
                        sprintf(tmp, "%s%s%s = %s - %s\n\t", tmpT1.getCode().c_str(), tmpT2.getCode().c_str(),
                                tmpE.getPlace().c_str(), tmpT1.getPlace().c_str(), tmpT2.getPlace().c_str());
                        tmpE.setCode(tmp);
                        tmpT1.setCode(tmpE.getCode());
                        tmpT1.setPlace(tmpE.getPlace());
                    }
                }
            }
            else break;
        }
        if(isFirst){
            tmpE.setCode(tmpT1.getCode());
            tmpE.setPlace(tmpT1.getPlace());
        }
        return true;
    }
    else{
        return false;
    }
}

bool MyFunction_T(std::ifstream& fp,str_T &tmpT){
    str_F tmpF1;
    str_F tmpF2;
    bool isFirst = true;
    if(MyFunction_F(fp,tmpF1)){
        while (1){
            if(node.getType()=="*") {
                isFirst = false;
                if(getNext(fp)){
                    if (MyFunction_F(fp, tmpF2)) {
                        tmpT.setPlace(newtemp());
                        char tmp[500];
                        sprintf(tmp, "%s%s%s = %s * %s\n\t", tmpF1.getCode().c_str(), tmpF2.getCode().c_str(),
                                tmpT.getPlace().c_str(), tmpF1.getPlace().c_str(), tmpF2.getPlace().c_str());
                        tmpT.setCode(tmp);
                        tmpF1.setCode(tmpT.getCode());
                        tmpF1.setPlace(tmpT.getPlace());
                    }
                }
            }
            else if(node.getType()=="/"){
                isFirst = false;
                if(getNext(fp)){
                    if (MyFunction_F(fp, tmpF2)) {
                        tmpT.setPlace(newtemp());
                        char tmp[500];
                        sprintf(tmp, "%s%s%s = %s / %s\n\t", tmpF1.getCode().c_str(), tmpF2.getCode().c_str(),
                                tmpT.getPlace().c_str(), tmpF1.getPlace().c_str(), tmpF2.getPlace().c_str());
                        tmpT.setCode(tmp);
                        tmpF1.setCode(tmpT.getCode());
                        tmpF1.setPlace(tmpT.getPlace());
                    }
                }
            }
            else break;
        }
        if(isFirst) {
            tmpT.setCode(tmpF1.getCode());
            tmpT.setPlace(tmpF1.getPlace());
        }
        return true;
    }
    else{
        return false;
    }
}

bool MyFunction_F(std::ifstream& fp,str_F &tmpF){
    str_E tmpE;
    if(node.getType()=="(") {
        if (getNext(fp)) {
            if (MyFunction_E(fp, tmpE)) {
                if (node.getType() == ")") {
                    tmpF.setPlace(tmpE.getPlace());
                    tmpF.setCode(tmpE.getCode());
                    if (getNext(fp)) {
                        return true;
                    }
                }
            }
        }
    }
    else if(node.getType()=="INT10"){
        tmpF.setPlace(node.getValue());
        if(getNext(fp)){
            return true;
        }
    }
    else if(node.getType()=="INT16"){
        tmpF.setPlace(node.getValue());
        if(getNext(fp)){
            return true;
        }
    }
    else if(node.getType()=="INT8"){
        tmpF.setPlace(node.getValue());
        if(getNext(fp)){
            return true;
        }
    }
    else if(node.getType()=="IDN"){
        tmpF.setPlace(node.getValue());
        if(getNext(fp)){
            return true;
        }
    }
    return false;
}

int main() {
    std::ifstream in_file;
    std::ofstream out_file;
    in_file.open("G:\\Desktop\\compile\\compile\\exp2\\in.txt");
    out_file.open("out.txt");
    while (scan(in_file,out_file));
    in_file.close();
    out_file.close();
    in_file.open("out.txt");
    out_file.open("out1.txt");
    int num = 1;
    do {
        label = 1;
        temp = 1;
        str_S tmpS;
        tmpS.setNext(0);
        if (MyFunction_S(in_file, tmpS)) {
            std::cout << "--------------------------\nsentence " << num << ":\n" ;
            out_file << "--------------------------\nsentence " << num++ << ":\n" ;
            std::cout << tmpS.getCode() << "L0:\t//S.next" << std::endl;
            out_file << tmpS.getCode() << "L0:\t//S.next" << std::endl;
        }
    } while (!node.getType().empty());
    in_file.close();
    out_file.close();
    return 0;
}
